﻿using Sidvall.Collections;
using System.Collections.Generic;
using System.Linq;

namespace Sidvall.Data
{
    public abstract class EntityManagerBase
    {
        protected abstract void AcceptItemChanges(IDataRowContextProvider item, Sidvall.Data.AcceptChangesSettings acceptChangesSettings);
        protected abstract IDataRowContextProvider CopyItem(IDataRowContextProvider destination, CopySettings copySettings, string parent, string child);
        protected abstract void ImportItem(IDataRowContextProvider destination, IDataRowContextProvider source, Sidvall.Data.CopySettings copySettings, string parent, string child);
        protected abstract bool IsDirtyItem(IDataRowContextProvider item);
        protected abstract void InitializeItem(IDataRowContextProvider item, InitializeSettings settings);

        #region AcceptItemsChanges

        protected void AcceptItemsChanges(IEnumerable<IDataRowContextProvider> value, Sidvall.Data.AcceptChangesSettings acceptChangesSettings)
        {
            if (value == null)
                return;
            var listContextProvider = value as Sidvall.Data.IListContextProvider;
            if (listContextProvider != null)
                listContextProvider.DeletedItemKeys.Clear();
            foreach (var item in value)
                AcceptItemChanges(item, acceptChangesSettings);
        }

        #endregion
        #region ImportItems

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        protected void ImportItems(IEnumerable<IDataRowContextProvider> destination, IEnumerable<IDataRowContextProvider> source,
            Sidvall.Data.CopySettings copySettings, string parent, string child)
        {
            IDataRowContextProvider item;
            Sidvall.Business.BusinessCollectionSettings businessSettings;

            if (destination == null)
                return;
            if (source == null)
                return;
            if (copySettings == null)
                copySettings = new Sidvall.Data.CopySettings();
            else if (copySettings.MergeOption == Sidvall.Data.MergeOption.DoNothing)
                return;
            if (copySettings.RaisePropertyChanged)
                businessSettings = Sidvall.Business.BusinessCollectionSettings.CreateSettingsAll();
            else
                businessSettings = Sidvall.Business.BusinessCollectionSettings.CreateSettingsNone();
            var sourceListContextProvider = source as Sidvall.Data.IListContextProvider;
            using (var businessCollectionEventManager = new Sidvall.Business.BusinessCollectionSettingsManager(destination as Sidvall.Business.IBusinessCollectionSettingsProvider, businessSettings))
            {
                switch (copySettings.MergeOption)
                {
                    case Sidvall.Data.MergeOption.Merge:
                        var index = source.ToDictionary(o => o.DataRowContextKey, true, false);
                        var deletedItems = new List<IDataRowContextProvider>();
                        var updatedItemIndex = new HashSet<string>();

                        // Update & Delete
                        foreach (var destinationItem in destination)
                        {
                            var key = destinationItem.DataRowContextKey;
                            if ((key != null) && (index.TryGetValue(key, out item)))
                            {
                                ImportItem(destinationItem, item, copySettings, parent, child);
                                updatedItemIndex.Add(key);
                            }
                            else if (destinationItem.DataRowStateIsDeleted)
                            {
                                deletedItems.Add(destinationItem);
                            }
                            else if ((sourceListContextProvider != null) && (sourceListContextProvider.DeletedItemKeys.Contains(key)))
                            {
                                deletedItems.Add(destinationItem);
                            }
                        }
                        foreach (var deletedItem in deletedItems)
                            destination.Remove(deletedItem);

                        // Add
                        foreach (var sourceItem in index.Values)
                        {
                            if (updatedItemIndex.Contains(sourceItem.DataRowContextKey))
                                continue;
                            item = CopyItem(sourceItem, copySettings, parent, child);
                            if (item != null)
                                destination.Add(item);
                        }
                        foreach (var sourceItem in source)
                        {
                            if (sourceItem.DataRowContextKey != null)
                                continue;
                            item = CopyItem(sourceItem, copySettings, parent, child);
                            if (item != null)
                                destination.Add(item);
                        }
                        break;
                    case Sidvall.Data.MergeOption.Append:
                        foreach (var sourceItem in source)
                        {
                            item = CopyItem(sourceItem, copySettings, parent, child);
                            if (item != null)
                                destination.Add(item);
                        }
                        if (copySettings.RowFilter.HasFlag(Sidvall.Data.DataRowFilters.Deleted))
                        {
                            if (sourceListContextProvider != null)
                            {
                                var destinationListContextProvider = destination as Sidvall.Data.IListContextProvider;
                                if (destinationListContextProvider != null)
                                {
                                    foreach (var sourceItemKey in sourceListContextProvider.DeletedItemKeys)
                                        destinationListContextProvider.DeletedItemKeys.Add(sourceItemKey);
                                }
                            }
                        }
                        break;
                    default:
                        break;
                }
            }
            var sourceTotalRowCount = source as Sidvall.Data.ITotalRowCount;
            if (sourceTotalRowCount != null)
            {
                var destinationTotalRowCount = destination as Sidvall.Data.ITotalRowCount;
                if (destinationTotalRowCount != null)
                    destinationTotalRowCount.TotalRowCount = sourceTotalRowCount.TotalRowCount;
            }
        }

        #endregion
        #region InitializeItems

        protected void InitializeItems(IEnumerable<IDataRowContextProvider> value, Sidvall.Data.InitializeSettings settings)
        {
            if (value == null)
                return;
            foreach (var item in value)
                InitializeItem(item, settings);
        }

        #endregion
        #region IsDirtyItems

        protected bool IsDirtyItems(IEnumerable<IDataRowContextProvider> value)
        {
            if (value == null)
                return false;
            var listContextProvider = value as Sidvall.Data.IListContextProvider;
            if ((listContextProvider != null) && (listContextProvider.DeletedItemKeys.Any()))
                return true;
            foreach (var item in value)
            {
                if (IsDirtyItem(item))
                    return true;
            }
            return false;
        }

        #endregion
    }
}
